/**
 * @description: 通用的AJAX请求封装，
 * @return 使用方式 import Request, { $request, $get, $post, $put, $delete } from '@/utils/request';
 */
import axios from 'axios';
import { showToast } from 'vant';

import type { AxiosInstance, AxiosRequestConfig, AxiosResponse, AxiosError, Canceler, InternalAxiosRequestConfig } from 'axios';

import { LocalStorageService, SessionStorageService } from '@/utils/storage';

const {
  DEV: isDev,
  VITE_APP_NODE_ENV: appNodeEnv,
  VITE_APP_PROXY_GATEWAY: proxyKey,
  VITE_APP_BASE_API: baseApi,
  VITE_APP_GAIA_API_KEY: apiKey,
} = import.meta.env;
const sourceAjaxList: Canceler[] = [];

const clearStorage = () => {
  LocalStorageService.clear();
  SessionStorageService.clear();
};

/**
 * @description: 需要主动调用new Request(), 可传递更多配置参数进去
 * @return {*}
 */
export class Request {
  instance: AxiosInstance;

  baseConfig: AxiosRequestConfig = {
    headers: {
      'X-Gaia-Api-Key': apiKey,
    },
    // eslint-disable-next-line no-nested-ternary
    baseURL: isDev
      ? proxyKey
      // eslint-disable-next-line no-negated-condition
      : (appNodeEnv === 'tk-uat' || appNodeEnv === 'tk-production')
        ? proxyKey
        : baseApi,
    timeout: 30000,
    withCredentials: !(appNodeEnv === 'tk-uat' || appNodeEnv === 'tk-production'),
  };

  constructor(axiosConfig?: AxiosRequestConfig) {
    this.instance = axios.create(Object.assign(this.baseConfig, axiosConfig));

    this.instance.interceptors.request.use(
      (requestConfig: InternalAxiosRequestConfig) => {
        // 设置 cancel token  用于取消请求 (当一个接口出现401后，取消后续多有发起的请求，避免出现好几个错误提示)
        requestConfig.cancelToken = new axios.CancelToken((cancel: Canceler): void => {
          sourceAjaxList.push(cancel);
        });

        // 这里一个token，你需要在这里取到你设置好的token的值 示例逻辑代码
        const token = SessionStorageService.get('_user_info')?.authorization || '';
        const tenantId = SessionStorageService.get('_user_info')?.currentTenantId || '';
        if (token) {
          // 这里将token设置到headers中，header的key是Authorization，这个key值根据你的需要进行修改即可
          requestConfig.headers = {
            Authorization: token,
            tenantId,
            ...requestConfig.headers,
          } as any;
        }

        requestConfig.paramsSerializer = {
          indexes: null,
        };

        return requestConfig;
      },
      (error: any) => Promise.reject(error)
    );

    this.instance.interceptors.response.use(
      (response: AxiosResponse) => {
        const requestUrl = response.config.url || '';
        const { headers, data: result } = response;

        // 后端直接返回文件流
        if (!headers['content-type']?.includes('application/json') || result instanceof Blob) {
          return response;
        }

        // 返回数据的code 不为200即为数据出错
        if (+result.code === 0 || +result.code === 200) {
          return result;
        }
        if (+result.code === 401) {
          clearStorage();
          showToast('权限校验失效，需要重新登录');
          throw new Error(`${result.loginUrl} 需要重新登录`);
        }
        if (+result.code === 403) {
          showToast(`${requestUrl} 暂无访问权限`);
          throw new Error(`${requestUrl} 暂无访问权限`);
        }

        showToast(result.message || result.msg || 'Error');
        throw new Error(result.message || result.msg || 'Error'); // 预期结果失败
      },
      (err: AxiosError) => {
        const requestUrl = err?.config?.url || '';
        if (err.toString().includes('timeout')) {
          showToast('请求超时，请稍后再试');
        }
        if (err && err.response) {
          const { status, data, config }: any = err.response;
          switch (status) {
          case 400:
            err.message = '请求错误';
            break;

          case 401:
            // 用户登录信息认证失败，退出到登录页之前清空缓存内容
            clearStorage();

            location.href = data.loginUrl || '';
            // 出现未登录或登录失效取消后面的请求
            if (sourceAjaxList.length > 0) {
              sourceAjaxList.forEach((ajaxCancel, index) => {
                ajaxCancel(); // 取消请求
                delete sourceAjaxList[index];
              });
            }
            break;

          case 403:
            err.message = `${requestUrl} 暂无访问权限`;
            break;

          case 404:
            err.message = `请求地址出错: ${config.url}`;
            break;

          case 408:
            err.message = '请求超时';
            break;

          case 500:
          case 501:
          case 502:
          case 503:
          case 504:
            err.message = '服务器开个小差，请稍后再试';
            break;

          case 505:
            err.message = 'HTTP版本不受支持';
            break;

          default:
            err.message = '请求失败，请稍后再试';
          }
          showToast(err.message || 'Error');
        }
        return Promise.reject(err);
      }
    );
  }

  public request<T = any>(config: AxiosRequestConfig): Promise<T> {
    return this.instance.request(config);
  }

  public get<T = any>(
    url: string,
    config?: AxiosRequestConfig
  ): Promise<T> {
    return this.instance.get(url, config);
  }

  public post<T = any>(
    url: string,
    data?: TypeObject,
    config?: AxiosRequestConfig
  ): Promise<T> {
    return this.instance.post(url, data, config);
  }

  public put<T = any>(
    url: string,
    data?: TypeObject,
    config?: AxiosRequestConfig
  ): Promise<T> {
    return this.instance.put(url, data, config);
  }

  public delete<T = any>(
    url: string,
    config?: AxiosRequestConfig
  ): Promise<T> {
    return this.instance.delete(url, config);
  }
}

/**
 * @description: 封装默认配置的axios的request、get、post、put、delete
 * @return {*}
 */
const server = new Request();
const $request = <T = any>(config: AxiosRequestConfig): Promise<T> => server.request(config);

const $get = <T = any>(
  url: string,
  config?: AxiosRequestConfig
): Promise<T> => server.get(url, config);

const $post = <T = any>(
  url: string,
  data?: TypeObject,
  config?: AxiosRequestConfig
): Promise<T> => server.post<T>(url, data, config);

const $put = <T = any>(
  url: string,
  data?: TypeObject,
  config?: AxiosRequestConfig
): Promise<T> => server.put<T>(url, data, config);

const $delete = <T = any>(
  url: string,
  config?: AxiosRequestConfig
): Promise<T> => server.delete<T>(url, config);

export {
  $request,
  $get,
  $post,
  $put,
  $delete,
};
export default server;
